package io.github.rascaler.dcenter.controller;

import io.github.rascaler.dcenter.annotation.OuterResponseBody;
import io.github.rascaler.dcenter.constant.ConstantItem;
import io.github.rascaler.dcenter.constant.KlineConstant;
import io.github.rascaler.dcenter.platforms.DbMapper;
import io.github.rascaler.dcenter.platforms.binance.BinanceDbMapperImpl;
import io.github.rascaler.dcenter.pojo.Candlestick;
import io.github.rascaler.dcenter.db.DbManager;
import io.github.rascaler.dcenter.pojo.Instrument;
import io.github.rascaler.dcenter.pojo.Rank;
import io.github.rascaler.dcenter.pojo.query.BatchKlineQuery;
import io.github.rascaler.dcenter.pojo.query.InstrumentQuery;
import io.github.rascaler.dcenter.pojo.query.KlineQuery;
import io.github.rascaler.dcenter.pojo.query.RankQuery;
import io.github.rascaler.dcenter.utils.DateUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static io.github.rascaler.assertj.Assertions.assertThat;

/**
 * @ClassName BinanceController
 * @Description TODO
 * @Author wurenqing
 * @Date 2023-12-01 17:01
 */

@Slf4j
@Api(tags = "币安接口")
@Controller
@RequestMapping("binance")
public class BinanceController {

    @Autowired
    private DbManager dbManager;

    @Resource(type = BinanceDbMapperImpl.class)
    private DbMapper binanceDbMapper;

    @ApiOperation("获取单个品种K线")
    @OuterResponseBody
    @GetMapping("klines")
    public List<Candlestick> klines(KlineQuery query) {
        String key = String.format(KlineConstant.binanceKeyTemplate, query.getSymbol(), query.getFrequency(), KlineConstant.ADJUST_NO, KlineConstant.BINANCE);
        List<Candlestick> list = dbManager.getMdb().getBarMap().get(key);
        if (CollectionUtils.isEmpty(list)) {
            return list;
        }
        if (null != query.getStartTime() && null != query.getEndTime()) {
            list = list.stream().filter(c -> c.getBarDate() >= query.getStartTime() && c.getBarDate() <= query.getEndTime()).collect(Collectors.toList());
        } else {
            if (null != query.getStartTime()) {
                list = list.stream().filter(c -> c.getBarDate() >= query.getStartTime()).collect(Collectors.toList());
            }
            if (null != query.getEndTime()) {
                list = list.stream().filter(c -> c.getBarDate() <= query.getEndTime()).collect(Collectors.toList());
            }
        }

        if (null != query.getLimit()) {
            list = list.subList(list.size() - query.getLimit(), list.size()).stream().collect(Collectors.toList());
        }
        return list;
    }

    @ApiOperation("获取多品种K线")
    @OuterResponseBody
    @PostMapping("batchKlines")
    public Map<String, List<Candlestick>> batchKlines(@RequestBody BatchKlineQuery query) {
        Map<String, List<Candlestick>> result = new HashMap<>();
        List<String> symbols = query.getSymbols();
        if (CollectionUtils.isEmpty(symbols)) {
            symbols = dbManager.getMdb().getInstrumentMap().values().stream().map(Instrument::getSymbol).collect(Collectors.toList());
        }
        for (String symbol  : symbols) {
            String key = String.format(KlineConstant.binanceKeyTemplate, symbol, query.getFrequency(), KlineConstant.ADJUST_NO, KlineConstant.BINANCE);
            List<Candlestick> list = dbManager.getMdb().getBarMap().get(key);
            if (CollectionUtils.isEmpty(list)) {
                result.put(symbol, list);
                continue;
            }
            if (null != query.getStartTime() && null != query.getEndTime()) {
                list = list.stream().filter(c -> c.getBarDate() >= query.getStartTime() && c.getBarDate() <= query.getEndTime()).collect(Collectors.toList());
            } else {
                if (null != query.getStartTime()) {
                    list = list.stream().filter(c -> c.getBarDate() >= query.getStartTime()).collect(Collectors.toList());
                }
                if (null != query.getEndTime()) {
                    list = list.stream().filter(c -> c.getBarDate() <= query.getEndTime()).collect(Collectors.toList());
                }
            }
            if (CollectionUtils.isEmpty(list)) {
                result.put(symbol, list);
                continue;
            }

            if (null != query.getLimit()) {
                list = list.subList(list.size() - query.getLimit(), list.size()).stream().collect(Collectors.toList());
            }
            result.put(symbol, list);
        }
        return result;
    }

    @ApiOperation("获取所有商品列表")
    @OuterResponseBody
    @GetMapping("listAllInstruments")
    public List<Instrument> listAllInstruments() {
        return binanceDbMapper.selectAllInstruments();
    }

    @ApiOperation("获取最后一根线的时间表")
    @OuterResponseBody
    @GetMapping("getTimeMap")
    public Map<String, Long>  getTimeMap(@RequestParam String frequency) {
        return binanceDbMapper.getTimeMap(frequency);
    }

    @ApiOperation("获取相对今日涨幅实时排名")
    @OuterResponseBody
    @GetMapping("listRank")
    public void  listRank(@RequestBody RankQuery query) {
    }

    @ApiOperation("查询今日排名达到要求的最早时间")
    @OuterResponseBody
    @PostMapping("listEarliestRank")
    public List<Rank>  listEarliestRank(@RequestBody RankQuery query) {
        Map<String, Candlestick> first4h = new HashMap<>();
        dbManager.getMdb().getInstrumentMap().values().forEach(d -> {
            String key = String.format(KlineConstant.binanceKeyTemplate, d.getSymbol(), ConstantItem.FREQUENCY_4HOUR, KlineConstant.ADJUST_NO, KlineConstant.BINANCE);
            List<Candlestick> list = dbManager.getMdb().getBarMap().get(key);
            list.stream().filter(l -> l.getBarDate().equals(DateUtil.today().getTime())).findFirst().ifPresent(l -> first4h.put(d.getSymbol(),l));
        });
        Map<String, Rank> result = new HashMap<>();
        List<Rank> ranks = new ArrayList<>();
        Date today = DateUtil.today();
        AtomicLong temp = new AtomicLong(DateUtil.today().getTime());
        while (temp.get() < DateUtil.currentMinute()) {
            ranks.clear();
            log.info("时间：{}" ,new Date(temp.get()));
            dbManager.getMdb().getInstrumentMap().values().forEach(d -> {
                Candlestick first = first4h.get(d.getSymbol());
                // 1分钟数据计算涨幅和今日涨幅
                String key = String.format(KlineConstant.binanceKeyTemplate, d.getSymbol(), query.getFrequency(), KlineConstant.ADJUST_NO, KlineConstant.BINANCE);
                List<Candlestick> list = dbManager.getMdb().getBarMap().get(key);
                list.stream().filter(l -> l.getBarDate().equals(temp.get())).forEach(l -> {
                    Rank rank = new Rank();
                    rank.setCandlestick(l);
                    rank.setSymbol(d.getSymbol());
                    rank.setFrequency(query.getFrequency());
                    rank.setPctChange(l.getClose().subtract(l.getOpen()).divide(l.getOpen(), 4, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100)));
                    rank.setDailyPctChange(l.getClose().subtract(first.getOpen()).divide(first.getOpen(), 4, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100)));
                    ranks.add(rank);
                });

            });
            // 根据涨幅排序并设置排名
            ranks.sort((r1, r2) -> r2.getDailyPctChange().compareTo(r1.getDailyPctChange()));
            for (int i = 0; i < ranks.size(); i++) {
                Rank rank = ranks.get(i);
                rank.setRankUp(i + 1);
                result.put(rank.getSymbol(), rank);
            }
            if (query.getRankOrder().equals("down")) {
                Stream<Rank> opt = ranks.stream().filter(r -> r.getRankDown() <= query.getRankNum());
                if (StringUtils.isNotBlank(query.getPctChangeOperator())) {
                    switch (query.getPctChangeOperator()) {
                        case ">":
                            opt = opt.filter(r -> r.getDailyPctChange().compareTo(new BigDecimal(query.getPctChange())) > 0);
                            break;
                        case ">=":
                            opt = opt.filter(r -> r.getDailyPctChange().compareTo(new BigDecimal(query.getPctChange())) >= 0);
                            break;
                        case "<":
                            opt = opt.filter(r -> r.getDailyPctChange().compareTo(new BigDecimal(query.getPctChange())) < 0);
                            break;
                        case "<=":
                            opt = opt.filter(r -> r.getDailyPctChange().compareTo(new BigDecimal(query.getPctChange())) <= 0);
                            break;
                        case "=":
                            opt = opt.filter(r -> r.getDailyPctChange().compareTo(new BigDecimal(query.getPctChange())) == 0);
                            break;
                    }
                }
                opt.forEach(r -> {
                    result.putIfAbsent(r.getSymbol(), r);
                });
            } else {
                Stream<Rank> opt = ranks.stream().filter(r -> r.getRankUp() <= query.getRankNum());
                if (StringUtils.isNotBlank(query.getPctChangeOperator())) {
                    switch (query.getPctChangeOperator()) {
                        case ">":
                            opt = opt.filter(r -> r.getDailyPctChange().compareTo(new BigDecimal(query.getPctChange())) > 0);
                            break;
                        case ">=":
                            opt = opt.filter(r -> r.getDailyPctChange().compareTo(new BigDecimal(query.getPctChange())) >= 0);
                            break;
                        case "<":
                            opt = opt.filter(r -> r.getDailyPctChange().compareTo(new BigDecimal(query.getPctChange())) < 0);
                            break;
                        case "<=":
                            opt = opt.filter(r -> r.getDailyPctChange().compareTo(new BigDecimal(query.getPctChange())) <= 0);
                            break;
                        case "=":
                            opt = opt.filter(r -> r.getDailyPctChange().compareTo(new BigDecimal(query.getPctChange())) == 0);
                            break;
                    }
                }
                opt.forEach(r -> {
                    result.putIfAbsent(r.getSymbol(), r);
                });
            }
            temp.set(temp.get() + 60000);
        }
        return new ArrayList<>(result.values());
    }


}
